import processing.opengl.*;

import traer.physics.*;
import traer.animation.*;
import processing.xml.*;

final float NODE_SIZE = 1;
final float EDGE_LENGTH = 5;
final float EDGE_STRENGTH = 0.1;
final float SPACER_STRENGTH = 1000;
float sval = 1.0;
float nmx, nmy;

ParticleSystem physics;
Smoother3D centroid;
XMLElement xml;
Map idmap;
PFont font;

void setup()
{
  size( 800, 800, OPENGL );
  smooth();
  frameRate( 24 );
  strokeWeight( 2 );
  ellipseMode( CENTER );
  font = loadFont("Georgia-16.vlw");
  //font = loadFont("Ziggurat-HTF-Black-32.vlw");
  textFont(font);
  
  physics = new ParticleSystem( 0, 0.25 );    // new ParticleSystem( float gravityY, float drag )
  centroid = new Smoother3D( 0.8 );           // see http://www.cs.princeton.edu/~traer/animation/
  idmap = new HashMap();
      
  xmlParse("wiki.xml");
}

void xmlParse( String xmlfile )
{
   //physics.clear();
  centroid.setValue( 0, 0, 1.0 );
   long mac = 10742340786l;
  xml = new XMLElement(this, "uncyc-links.xml");
  int numSites = xml.getChildCount();
  for (int i = 0; i < numSites; i++) {
    XMLElement kid = xml.getChild(i);
    String idString = kid.getStringAttribute("id");
      long id = Long.parseLong(idString.trim());
    String url = kid.getStringAttribute("url");
    String title = kid.getStringAttribute("title");
    String hotnessString = kid.getStringAttribute("hotness");
      double hotness = Double.parseDouble(hotnessString.trim());
    int neighbors = kid.getIntAttribute("neighbors");
    //String site = kid.getContent();
    println(id + " : " + url + " : " + hotness);
    int particleid = physics.addPageParticle( new PageParticle( id, title, url, hotness, neighbors ) );
    idmap.put(new Long(id), new Integer(particleid) );
  }
  for ( int i = 0; i < numSites; i++ ) {
    XMLElement page = xml.getChild(i);
    String idString = page.getStringAttribute("id");
      long id = Long.parseLong(idString.trim());
    for ( int j = 0; j < page.getChildCount(); j++ ) {
      XMLElement link = page.getChild(j);
      String other_idString = link.getStringAttribute("other_id");
        long other_id = Long.parseLong(other_idString.trim());
      int weight = link.getIntAttribute("weight");
      if( weight > 2 ) {
        PageParticle a = (PageParticle)physics.getParticle( ((Integer)(idmap.get( new Long(id) ))).intValue() );
        PageParticle b = (PageParticle)physics.getParticle( ((Integer)(idmap.get( new Long(other_id) ))).intValue() );
        //a.neighbors++;
        //b.neighbors++;
        addSpacersToNode( a, b );
        physics.makeSpring( a, b, EDGE_STRENGTH, EDGE_STRENGTH, (a.neighbors*EDGE_LENGTH) );
        a.moveTo( b.position().x() + random( -1, 1 ), a.position().y() + random( -1, 1 ), 0 );
      }
    }
    
  }
   
   //PageParticle pp1 = new PageParticle( 1, "top", "url", 2 );      // param: page id, title, url, neighbors
   //PageParticle pp2 = new PageParticle( 2, "second", "url", 2 );
   //addNodes( pp1, pp2 );
}

void draw()
{
  physics.tick( 1.0 ); 
  if ( physics.numberOfParticles() > 1 )
    updateCentroid();
  centroid.tick();
  background( 255 );
  translate( width/2 , height/2 );
  scale( centroid.z() );
  translate( -centroid.x(), -centroid.y() );
   
  drawZoom();
 
  drawNetwork();  
}

void drawZoom(){
  if (abs(mouseX - nmx) > 1.0) { 
    nmx = nmx + (mouseX-nmx)/2; 
  }
  if (abs(mouseY - nmy) > 1.0) { 
    nmy += (mouseY-nmy)/2; 
  }
  
  if(mousePressed) { 
    sval += 0.15; 
  } else {
    sval -= 0.3; 
  }
  if(sval > 4.5) { sval = 4.5; }
  if(sval < 1.0) { sval = 1.0; }
  
  translate(width/2+nmx*sval-100, height/2+nmy*sval-100, -50);
  scale(sval);
}

void drawNetwork()
{      
  // draw vertices
  fill( 160 );
  noStroke();
  //print( physics.numberOfParticles() );
  for ( int i = 0; i < physics.numberOfParticles(); ++i )
  {
    PageParticle v = (PageParticle)physics.getParticle( i );
    stroke(0);
    fill( (int)(255*v.heat) );
    ellipse( v.position().x(), v.position().y(), v.neighbors*NODE_SIZE, v.neighbors*NODE_SIZE );
    if(v.neighbors > 30) {
      fill( (int)(128*v.heat) );
      text(v.title, v.position().x(), v.position().y() );
    }
  }

  // draw edges 
  stroke( 0 );
  strokeWeight(0.5);
  beginShape( LINES );
  for ( int i = 0; i < physics.numberOfSprings(); ++i )
  {
    Spring e = physics.getSpring( i );
    Particle a = e.getOneEnd();
    Particle b = e.getTheOtherEnd();
    vertex( a.position().x(), a.position().y() );
    vertex( b.position().x(), b.position().y() );
  }
  endShape();
}


void updateCentroid()
{
  float 
    xMax = Float.NEGATIVE_INFINITY, 
    xMin = Float.POSITIVE_INFINITY, 
    yMin = Float.POSITIVE_INFINITY, 
    yMax = Float.NEGATIVE_INFINITY;

  for ( int i = 0; i < physics.numberOfParticles(); ++i )
  {
    Particle p = physics.getParticle( i );
    xMax = max( xMax, p.position().x() );
    xMin = min( xMin, p.position().x() );
    yMin = min( yMin, p.position().y() );
    yMax = max( yMax, p.position().y() );
  }
  float deltaX = xMax-xMin;
  float deltaY = yMax-yMin;
  if ( deltaY > deltaX )
    centroid.setTarget( xMin + 0.5*deltaX, yMin +0.5*deltaY, height/(deltaY+50) );
  else
    centroid.setTarget( xMin + 0.5*deltaX, yMin +0.5*deltaY, width/(deltaX+50) );
}

void addSpacersToNode( Particle p, Particle r )
{
  for ( int i = 0; i < physics.numberOfParticles(); ++i )
  {
    Particle q = physics.getParticle( i );
    if ( p != q && p != r )
      physics.makeAttraction( p, q, -SPACER_STRENGTH, 20 );
  }
}

void makeEdgeBetween( Particle a, Particle b )
{
  physics.makeSpring( a, b, EDGE_STRENGTH, EDGE_STRENGTH, EDGE_LENGTH );
}

void addNodes(PageParticle p, PageParticle q)
{ 
  //Particle p = physics.makeParticle();
  //Particle q = physics.getParticle( (int)random( 0, physics.numberOfParticles()-1) );
  //while ( q == p )
    //q = physics.getParticle( (int)random( 0, physics.numberOfParticles()-1) );
  addSpacersToNode( p, q );
  makeEdgeBetween( p, q );
  p.moveTo( q.position().x() + random( -1, 1 ), q.position().y() + random( -1, 1 ), 0 );
  physics.addPageParticle(p);
  physics.addPageParticle(q);
}
